package com.appspot.peachykene.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentSkipListSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.appspot.peachykene.HerbDictionary;
import com.appspot.peachykene.HerbFact;

public class InMemoryHerbDictionary implements HerbDictionary {
	private static final Map<String, HerbFact> herbFacts;
	private static final List<String> en_herbNames;
	private static final List<String> nl_herbNames;
	private static final List<String> en_symptoms;
	private static final List<String> nl_symptoms;
	static {
		herbFacts = new LinkedHashMap<String, HerbFact>();
		en_herbNames = new ArrayList<String>();
		nl_herbNames = new ArrayList<String>();
		en_symptoms = new ArrayList<String>();
		nl_symptoms = new ArrayList<String>();
		
		/*
		HerbFact fennel = new HerbFact("Fennel", "/img/fennel_133_200.jpg");
		fennel.setDescription("Fennel tastes great and is less filling");
		fennel.addAllUses(Arrays.asList("antiseptic", "bad breath"));
		add(fennel);
		HerbFact sjw = new HerbFact("Saint John's Wart", "/img/saintjohnswart_129_200.jpg");
		sjw.setDescription("Its flowers measure up to 2.5 cm across, have five petals, and are colored bright yellow with conspicuous black dots.");
		sjw.addAllUses(Arrays.asList("depression"));
		add(sjw);
				
		Iterator<HerbFact> iter =  herbFacts.values().iterator();
		while(iter.hasNext()) {
			HerbFact fact = iter.next(); 
			herbNames.add(fact.getName());
			symptoms.addAll(fact.getUses());
		}
		*/
		populate();
	}
	
	private static void add(HerbFact herb) {
		for(String names : herb.getName("en").split(",")) {
			herbFacts.put("en_" + names.trim().toLowerCase(), herb);
		}
		for(String names : herb.getName("nl").split(",")) {
			herbFacts.put("nl_" + names.trim().toLowerCase(), herb);
		}				
	}
	
	public List<String> getAllHerbNames(String lang) {
		if("nl".equals(lang)) {
		    return nl_herbNames;
		} else {
			return en_herbNames;
		}
	}
	
    public List<String> getAllSymptoms(String lang) {
    	if("nl".equals(lang)) {
		    return nl_symptoms;
		} else {
			return en_symptoms;
		}
	}

    public SortedSet<HerbFact> getAllHerbs() {
    	SortedSet<HerbFact> alphabeticalHerbs = new ConcurrentSkipListSet<HerbFact>();
    	alphabeticalHerbs.addAll(herbFacts.values());
    	return alphabeticalHerbs;
    }
    
	public HerbFact get(String herbName, String lang) {
		return herbFacts.get(lang + "_" + herbName.toLowerCase());
	}

	@Override
	public Set<HerbFact> getBySymptom(String symptom, String lang) {
		Set<HerbFact> results = new HashSet<HerbFact>();
		Iterator<HerbFact> iter =  herbFacts.values().iterator();
		while(iter.hasNext()) {
			HerbFact fact = iter.next();
			if(fact.getConditions(lang).contains(symptom)) {
			  results.add(fact);
			}
		}
		return results;
	}
	
	private static void populate() {
		herbFacts.clear();
		en_herbNames.clear();
		nl_herbNames.clear();
		en_symptoms.clear();
		nl_symptoms.clear();
		
		InputStream is = null;
		try {
			is = InMemoryHerbDictionary.class.getClassLoader().getResourceAsStream("herbs.xml");
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

			dbf.setNamespaceAware(true);
			dbf.setValidating(false);
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(is);
			Element root = doc.getDocumentElement();
			NodeList herbs = root.getElementsByTagName("herb");
			for(int i=0; i < herbs.getLength(); i++) { 
				Element herb = (Element) herbs.item(i);
				HerbFact fact = new HerbFact();
				String[] elements = new String[] { "name", "image", "descriptionShort", "conditions" };
				for(int j=0; j < elements.length; j++) {
					
					NodeList list = herb.getElementsByTagName(elements[j]);
					if(list.getLength() != 1) {
						throw new IllegalStateException();
					}
							
					Element node = (Element) list.item(0);
					String nodeName = node.getNodeName();
					if("image".equals(nodeName)) {
						fact.setImage(node.getTextContent());
					} else if("name".equals(nodeName)) {
						fact.setName(getTagValue("en", node), "en");
						fact.setName(getTagValue("nl", node), "nl");
						fact.setLatinName(getTagValue("latin", node));
					} else if("descriptionShort".equals(nodeName)) {
						fact.setDescriptionShort(getTagValue("en", node), "en");
						fact.setDescriptionShort(getTagValue("nl", node), "nl");
					} else if("conditions".equals(nodeName)) {
						for(String lang : new String[] {"en", "nl"}) {							
							Collection<String> conditions = new HashSet<String>();
							String cond = getTagValue(lang, node);
							cond = (cond == null ? "" : cond);
							String[] conds = cond.split(",");
							for(String c : conds) {
								c = c.trim();
								if(!c.isEmpty()) {
									conditions.add(c.trim());
								}
							}
							fact.addAllConditions(conditions, lang);
						}
					}
				}				
				add(fact);
			}						
		} catch(Exception ex) {
			throw new RuntimeException(ex);
		} finally {
			try { if(is !=null) is.close(); }catch(IOException ignore){}
		}
		Set<HerbFact> uniqueHerbs = new HashSet<HerbFact>();
		uniqueHerbs.addAll(herbFacts.values());
		Iterator<HerbFact> iter =  uniqueHerbs.iterator();
		while(iter.hasNext()) {
			HerbFact fact = iter.next();
			for(String names : fact.getName("en").split(",")) {
				en_herbNames.add(names.trim());
			}
			for(String names : fact.getName("nl").split(",")) {
				nl_herbNames.add(names);
			}
			en_symptoms.addAll(fact.getConditions("en"));
			nl_symptoms.addAll(fact.getConditions("nl"));
		}
	}
	
	private static String getTagValue(String sTag, Element eElement) {
		NodeList nlList = eElement.getElementsByTagName(sTag);
		if (nlList.getLength() == 0) return null;
		String value = nlList.item(0).getTextContent();
		return (value == null || value.isEmpty()) ? null : value;
	}
}